package cn.sumpu.app.guoguojie.biz.impl.fastext;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
//import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
//import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wltea.analyzer.dic.Dictionary;
import org.wltea.analyzer.lucene.IKAnalyzer;
import org.wltea.analyzer.lucene.IKQueryParser;
import org.wltea.analyzer.lucene.IKSimilarity;
import org.wltea.analyzer.lucene.IKTokenizer;
import cn.sumpu.app.guoguojie.biz.FastextBiz;
import cn.sumpu.app.guoguojie.constant.SearchType;
import cn.sumpu.app.guoguojie.dao.DoingDao;
import cn.sumpu.app.guoguojie.dao.MemberDao;
import cn.sumpu.app.guoguojie.domain.Doing;
import cn.sumpu.app.guoguojie.domain.DoingAudit;
import cn.sumpu.app.guoguojie.domain.DoingProduct;
import cn.sumpu.app.guoguojie.domain.DoingProductAudit;
import cn.sumpu.app.guoguojie.domain.Member;
import cn.sumpu.app.guoguojie.domain.MemberAudit;
import cn.sumpu.app.guoguojie.util.Common;

public class FastextBizImpl implements FastextBiz {

    private final static Logger logger    = LoggerFactory.getLogger(FastextBizImpl.class);

    private String              indexDir;

    private MemberDao           memberDao;

    private DoingDao            doingDao;

    // 违禁词
    private List<String>        antiWords = new ArrayList<String>();

    // 扩展词
    private List<String>        extWords  = new ArrayList<String>();

    {
        try {
            InputStreamReader isr = new InputStreamReader(FastextBizImpl.class.getResourceAsStream("/antiWords.dic"));
            BufferedReader buf = new BufferedReader(isr);
            String str = buf.readLine();
            while (str != null) {
                antiWords.add(str);
                str = buf.readLine();
            }
            buf.close();
            isr.close();

            isr = new InputStreamReader(FastextBizImpl.class.getResourceAsStream("/extWords.dic"));
            buf = new BufferedReader(isr);
            str = buf.readLine();
            while (str != null) {
                extWords.add(str);
                str = buf.readLine();
            }
            buf.close();
            isr.close();

            Dictionary.loadExtendWords(extWords);
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("FastextBizImpl init fail.", e);
            }
        }
    }

    public void setDoingDao(DoingDao doingDao) {
        this.doingDao = doingDao;
    }

    public void setMemberDao(MemberDao memberDao) {
        this.memberDao = memberDao;
    }

    public void setIndexDir(String indexDir) {
        this.indexDir = indexDir;
    }

    @Override
    public void buildIndex() {
        if (logger.isDebugEnabled()) {
            logger.debug("Build index start...");
        }
        deleteIndex();
        // buildMemberIndex();
        // buildDoingIndex();
        buildDoingProductIndex();
        if (logger.isDebugEnabled()) {
            logger.debug("Build index end.");
        }
    }

    private void deleteIndex() {
        try {
            Directory dir = FSDirectory.open(new File(indexDir));
            IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_34, new IKAnalyzer());
            IndexWriter indexWriter = new IndexWriter(dir, conf);
            indexWriter.deleteAll();
            indexWriter.optimize();
            indexWriter.commit();
            indexWriter.close();
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Delete all index fail.", e);
            }
        }
    }

    @SuppressWarnings("unused")
    private void buildMemberIndex() {
        try {
            List<Member> memberList = memberDao.getMemberList(new HashMap<String, Object>());
            if (null != memberList && memberList.size() > 0) {
                Directory dir = FSDirectory.open(new File(indexDir));
                IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_34, new IKAnalyzer());
                conf.setOpenMode(OpenMode.CREATE);
                IndexWriter indexWriter = new IndexWriter(dir, conf);
                for (Member m : memberList) {
                    Document doc = new Document();
                    doc.add(new Field("nickname", m.getNickname(), Store.YES, Index.ANALYZED));
                    doc.add(new Field("memberid", m.getIdentifier(), Store.YES, Index.NOT_ANALYZED));
                    indexWriter.addDocument(doc);
                }
                indexWriter.optimize();
                indexWriter.commit();
                indexWriter.close();
            }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Build member index fail.", e);
            }
        }
    }

    @SuppressWarnings("unused")
    private void buildDoingIndex() {
        try {
            List<Doing> doingList = doingDao.getDoingAll();
            if (null != doingList && doingList.size() > 0) {
                Directory dir = FSDirectory.open(new File(indexDir));
                IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_34, new IKAnalyzer());
                conf.setOpenMode(OpenMode.CREATE);
                IndexWriter indexWriter = new IndexWriter(dir, conf);
                for (Doing d : doingList) {
                    Document doc = new Document();
                    doc.add(new Field("content", d.getContent(), Store.YES, Index.ANALYZED));
                    doc.add(new Field("doingid", d.getIdentifier(), Store.YES, Index.NOT_ANALYZED));
                    indexWriter.addDocument(doc);
                }
                indexWriter.optimize();
                indexWriter.commit();
                indexWriter.close();
            }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Build doing index fail.", e);
            }
        }
    }

    private void buildDoingProductIndex() {
        try {
            List<DoingProduct> productList = doingDao.getDoingProductList(new HashMap<String, Object>());
            doingDao.deleteDoingProductAudit(new HashMap<String, Object>());
            if (null != productList && productList.size() > 0) {
                Directory dir = FSDirectory.open(new File(indexDir));
                IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_34, new IKAnalyzer());
                conf.setOpenMode(OpenMode.CREATE);
                IndexWriter indexWriter = new IndexWriter(dir, conf);
                for (DoingProduct p : productList) {
                    Document doc = new Document();
                    doc.add(new Field("name", p.getName(), Store.YES, Index.ANALYZED));
                    doc.add(new Field("productid", p.getIdentifier(), Store.YES, Index.NOT_ANALYZED));
                    indexWriter.addDocument(doc);
                }
                indexWriter.optimize();
                indexWriter.commit();
                indexWriter.close();
            }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Build doing-product index fail.", e);
            }
        }
    }

    @Override
    public void refreshIndex() {
        if (logger.isDebugEnabled()) {
            logger.debug("Refresh index start...");
        }
        // refreshMemberIndex();
        // refreshDoingIndex();
        refreshDoingProductIndex();
        if (logger.isDebugEnabled()) {
            logger.debug("Refresh index end.");
        }
    }

    @SuppressWarnings("unused")
    private void refreshMemberIndex() {
        try {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("operation", "insert");
            List<MemberAudit> maList = memberDao.getMemberAuditList(map);
            if (null != maList && maList.size() > 0) {
                Directory dir = FSDirectory.open(new File(indexDir));
                IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_34, new IKAnalyzer());
                conf.setOpenMode(OpenMode.APPEND);
                IndexWriter indexWriter = new IndexWriter(dir, conf);
                StringBuilder ids = new StringBuilder();
                int i = 0;
                for (MemberAudit ma : maList) {
                    if (i != 0) {
                        ids.append(",");
                    }
                    ids.append("'");
                    ids.append(ma.getIdentifier());
                    ids.append("'");
                    i++;
                }
                map.clear();
                map.put("ids", ids.toString());
                List<Member> mList = memberDao.getMemberListByIds(map);
                for (Member m : mList) {
                    Document doc = new Document();
                    doc.add(new Field("nickname", m.getNickname(), Store.YES, Index.ANALYZED));
                    doc.add(new Field("memberid", m.getIdentifier(), Store.YES, Index.NOT_ANALYZED));
                    indexWriter.addDocument(doc);
                }
                indexWriter.optimize();
                indexWriter.commit();
                indexWriter.close();
                memberDao.deleteMemberAuditByIds(map);
            }

            // map.clear();
            // map.put("operation", "delete");
            // maList = memberDao.getMemberAuditList(map);
            // if (null != maList && maList.size() > 0) {
            // Directory dir = FSDirectory.open(new File(indexDir));
            // IndexReader indexReader = IndexReader.open(dir, false);
            // StringBuilder ids = new StringBuilder();
            // int i = 0;
            // for (MemberAudit ma : maList) {
            // if (i != 0) {
            // ids.append(",");
            // }
            // ids.append("'");
            // ids.append(ma.getIdentifier());
            // ids.append("'");
            // i++;
            //
            // Term term = new Term("memberid", ma.getIdentifier());
            // indexReader.deleteDocuments(term);
            // }
            // indexReader.close();
            //
            // map.clear();
            // map.put("ids", ids.toString());
            // memberDao.deleteMemberAuditByIds(map);
            // }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Refresh member index fail.", e);
            }
        }
    }

    @SuppressWarnings("unused")
    private void refreshDoingIndex() {
        try {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("operation", "insert");
            List<DoingAudit> daList = doingDao.getDoingAuditList(map);
            if (null != daList && daList.size() > 0) {
                Directory dir = FSDirectory.open(new File(indexDir));
                IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_34, new IKAnalyzer());
                conf.setOpenMode(OpenMode.APPEND);
                IndexWriter indexWriter = new IndexWriter(dir, conf);
                StringBuilder ids = new StringBuilder();
                int i = 0;
                for (DoingAudit da : daList) {
                    if (i != 0) {
                        ids.append(",");
                    }
                    ids.append("'");
                    ids.append(da.getIdentifier());
                    ids.append("'");
                    i++;
                }
                map.clear();
                map.put("ids", ids.toString());
                List<Doing> dList = doingDao.getDoingListByDoingIds(map);
                for (Doing d : dList) {
                    Document doc = new Document();
                    doc.add(new Field("content", d.getContent(), Store.YES, Index.ANALYZED));
                    doc.add(new Field("doingid", d.getIdentifier(), Store.YES, Index.NOT_ANALYZED));
                    indexWriter.addDocument(doc);
                }
                indexWriter.optimize();
                indexWriter.commit();
                indexWriter.close();
                doingDao.deleteDoingAuditByIds(map);
            }

            // map.clear();
            // map.put("operation", "delete");
            // daList = doingDao.getDoingAuditList(map);
            // if (null != daList && daList.size() > 0) {
            // Directory dir = FSDirectory.open(new File(indexDir));
            // IndexWriterConfig conf = new IndexWriterConfig(
            // Version.LUCENE_34, new IKAnalyzer());
            // IndexWriter indexWriter = new IndexWriter(dir, conf);
            // StringBuilder ids = new StringBuilder();
            // int i = 0;
            // for (DoingAudit da : daList) {
            // if (i != 0) {
            // ids.append(",");
            // }
            // ids.append("'");
            // ids.append(da.getIdentifier());
            // ids.append("'");
            // i++;
            // }
            // map.clear();
            // map.put("ids", ids.toString());
            // List<Doing> dList = doingDao.getDoingListByDoingIds(map);
            // for (Doing d : dList) {
            // Term term = new Term("doingid", d.getIdentifier());
            // indexWriter.deleteDocuments(term);
            // }
            // indexWriter.optimize();
            // indexWriter.commit();
            // indexWriter.close();
            // doingDao.deleteDoingAuditByIds(map);
            // }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Refresh doing index fail.", e);
            }
        }
    }

    private void refreshDoingProductIndex() {
        try {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("operation", "insert");
            List<DoingProductAudit> dpaList = doingDao.getDoingProductAuditList(map);
            if (null != dpaList && dpaList.size() > 0) {
                Directory dir = FSDirectory.open(new File(indexDir));
                IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_34, new IKAnalyzer());
                conf.setOpenMode(OpenMode.APPEND);
                IndexWriter indexWriter = new IndexWriter(dir, conf);
                StringBuilder ids = new StringBuilder();
                int i = 0;
                for (DoingProductAudit dpa : dpaList) {
                    if (i != 0) {
                        ids.append(",");
                    }
                    ids.append("'");
                    ids.append(dpa.getIdentifier());
                    ids.append("'");
                    i++;
                }
                map.clear();
                map.put("ids", ids.toString());
                List<DoingProduct> dpList = doingDao.getProductListByProductIds(map);
                for (DoingProduct dp : dpList) {
                    Document doc = new Document();
                    doc.add(new Field("name", dp.getName(), Store.YES, Index.ANALYZED));
                    doc.add(new Field("productid", dp.getIdentifier(), Store.YES, Index.NOT_ANALYZED));
                    indexWriter.addDocument(doc);
                }
                indexWriter.optimize();
                indexWriter.commit();
                indexWriter.close();
                doingDao.deleteDoingProductAuditByIds(map);
            }

            // map.clear();
            // map.put("operation", "delete");
            // dpaList = doingDao.getDoingProductAuditList(map);
            // if (null != dpaList && dpaList.size() > 0) {
            // Directory dir = FSDirectory.open(new File(indexDir));
            // IndexWriterConfig conf = new IndexWriterConfig(
            // Version.LUCENE_34, new IKAnalyzer());
            // IndexWriter indexWriter = new IndexWriter(dir, conf);
            // StringBuilder ids = new StringBuilder();
            // int i = 0;
            // for (DoingProductAudit dpa : dpaList) {
            // if (i != 0) {
            // ids.append(",");
            // }
            // ids.append("'");
            // ids.append(dpa.getIdentifier());
            // ids.append("'");
            // i++;
            // }
            // map.clear();
            // map.put("ids", ids.toString());
            // List<DoingProduct> dpList = doingDao
            // .getProductListByProductIds(map);
            // for (DoingProduct dp : dpList) {
            // Term term = new Term("productid", dp.getIdentifier());
            // indexWriter.deleteDocuments(term);
            // }
            // indexWriter.optimize();
            // indexWriter.commit();
            // indexWriter.close();
            // doingDao.deleteDoingProductAuditByIds(map);
            // }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Refresh doing-product index fail.", e);
            }
        }
    }

    @Override
    public Set<String> search(SearchType type, String keywords, boolean isPart) {
        Set<String> ret = new HashSet<String>();
        if (Common.emptyStr(keywords)) {
            if (logger.isWarnEnabled()) {
                logger.warn("Search incorrect,search-type=[" + type.getValue() + "keywords=[" + keywords + "]");
            }
            return ret;
        }
        if (SearchType.MEMBER.getValue().equals(type.getValue())) {
            return searchMember(keywords, isPart);
        }
        if (SearchType.DOING.getValue().equals(type.getValue())) {
            return searchDoing(keywords, isPart);
        }
        if (SearchType.PRODUCT.getValue().equals(type.getValue())) {
            return searchProduct(keywords, isPart);
        }
        throw new UnsupportedOperationException();
    }

    private Set<String> searchDoing(String keywords, boolean isPart) {
        Set<String> ret = new HashSet<String>();
        if (Common.emptyStr(keywords)) {
            if (logger.isWarnEnabled()) {
                logger.warn("Search doing incorrect,keywords=[" + keywords + "]");
            }
            return ret;
        }
        IndexSearcher indexSearcher = null;
        try {
            Query query = null;
            indexSearcher = new IndexSearcher(FSDirectory.open(new File(indexDir)));
            if (isPart) {
                indexSearcher.setSimilarity(new IKSimilarity());
                query = IKQueryParser.parse("content", keywords);
            } else {
                Term term = new Term("content", keywords);
                query = new TermQuery(term);
            }
            TopDocs topDocs = indexSearcher.search(query, 100);
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = indexSearcher.doc(scoreDoc.doc);
                ret.add(doc.get("doingid"));
            }
            indexSearcher.close();
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Search doing fail.", e);
            }
            if (null != indexSearcher) {
                try {
                    indexSearcher.close();
                } catch (IOException e1) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Search doing.", e);
                    }
                }
            }
        }
        return ret;
    }

    private Set<String> searchProduct(String keywords, boolean isPart) {
        Set<String> ret = new HashSet<String>();
        if (Common.emptyStr(keywords)) {
            if (logger.isWarnEnabled()) {
                logger.warn("Search doing-product incorrect,keywords=[" + keywords + "]");
            }
            return ret;
        }
        IndexSearcher indexSearcher = null;
        try {
            Query query = null;
            indexSearcher = new IndexSearcher(FSDirectory.open(new File(indexDir)));
            if (isPart) {
                indexSearcher.setSimilarity(new IKSimilarity());
                query = IKQueryParser.parse("name", keywords);
            } else {
                Term term = new Term("name", keywords);
                query = new TermQuery(term);
            }
            TopDocs topDocs = indexSearcher.search(query, 100000);
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = indexSearcher.doc(scoreDoc.doc);
                ret.add(doc.get("productid"));
            }
            indexSearcher.close();
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Search doing-product fail.", e);
            }
            if (null != indexSearcher) {
                try {
                    indexSearcher.close();
                } catch (IOException e1) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Search doing-product.", e);
                    }
                }
            }
        }
        return ret;
    }

    private Set<String> searchMember(String keywords, boolean isPart) {
        Set<String> ret = new HashSet<String>();
        if (Common.emptyStr(keywords)) {
            if (logger.isWarnEnabled()) {
                logger.warn("Search member incorrect,keywords=[" + keywords + "]");
            }
            return ret;
        }
        IndexSearcher indexSearcher = null;
        try {
            Query query = null;
            indexSearcher = new IndexSearcher(FSDirectory.open(new File(indexDir)));
            if (isPart) {
                indexSearcher.setSimilarity(new IKSimilarity());
                query = IKQueryParser.parse("nickname", keywords);
            } else {
                Term term = new Term("nickname", keywords);
                query = new TermQuery(term);
            }
            TopDocs topDocs = indexSearcher.search(query, 100);
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = indexSearcher.doc(scoreDoc.doc);
                ret.add(doc.get("memberid"));
            }
            indexSearcher.close();
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Search member fail.", e);
            }
            if (null != indexSearcher) {
                try {
                    indexSearcher.close();
                } catch (IOException e1) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Search member.", e);
                    }
                }
            }
        }
        return ret;
    }

    @Override
    public String antiReplace(String text) {
        for (int i = 0; i < antiWords.size(); i++) {
            if (text.contains(antiWords.get(i))) {
                text = text.replace(antiWords.get(i), "*");
            }
        }
        return text;
    }

    @Override
    public List<String> analyze(String text) {
        IKTokenizer tokenizer = new IKTokenizer(new StringReader(text), false);
        List<String> ret = new ArrayList<String>();
        try {
            while (tokenizer.incrementToken()) {
                CharTermAttribute termAtt = tokenizer.getAttribute(CharTermAttribute.class);
                ret.add(termAtt.toString());
            }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Text analyze fail.", e);
            }
        }
        return ret;
    }

    /**
     * @param type
     * @param keywordList
     * @return
     * @see cn.sumpu.app.guoguojie.biz.FastextBiz#search(cn.sumpu.app.guoguojie.constant.SearchType, java.util.List)
     */
    @Override
    public Set<String> search(SearchType type, List<Map<String, Object>> keywordList) {
        if (!SearchType.PRODUCT.getValue().equals(type.getValue())) {
            if (logger.isErrorEnabled()) {
                logger.error("Unsupported search type[" + type + "]");
            }
            return null;
        }
        if (null == keywordList || keywordList.size() <= 0) {
            if (logger.isErrorEnabled()) {
                logger.error("Unsupported keyword list[is null or size <= 0]");
            }
            return null;
        }
        Set<String> temp = new HashSet<String>();
        for (int i = 0; i < keywordList.size(); i++) {
            Map<String, Object> entry = keywordList.get(i);
            String keywords = (String) entry.get("keywords");
            Boolean isPart = (Boolean) entry.get("isPart");
            boolean part = false;
            if (null != isPart && isPart.booleanValue()) {
                part = true;
            }

            String[] keys = keywords.split(" ");
            Set<String> set = search(type, keys[0], part);
            for (int j = 0; j < keys.length; j++) {
                if (j != 0) {
                    Set<String> s = search(type, keys[j], part);
                    set.addAll(s);
                }
            }

            if (i != 0) {
                set.retainAll(temp);
            }
            temp = set;
        }
        return temp;
    }

}
